package com.moss.cloud.base.biz.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.moss.cloud.base.api.model.from.BaseFrom;
import com.moss.cloud.base.api.model.from.TenantFrom;
import com.moss.cloud.base.api.model.from.UpdateUserTenantFrom;
import com.moss.cloud.base.api.model.param.TenantPageParam;
import com.moss.cloud.base.api.model.vo.TenantInfoVO;
import com.moss.cloud.base.api.model.vo.TenantLegalVO;
import com.moss.cloud.base.api.model.vo.TenantPageVO;
import com.moss.cloud.base.biz.event.TenantInfoEvent;
import com.moss.cloud.base.biz.event.TenantStatusEvent;
import com.moss.cloud.base.biz.event.UpdateUserTenantEvent;
import com.moss.cloud.base.biz.mapper.SysTenantMapper;
import com.moss.cloud.base.biz.model.SysTenant;
import com.moss.cloud.base.biz.model.SysTenantLegal;
import com.moss.cloud.base.biz.service.ISysTenantLegalService;
import com.moss.cloud.base.biz.service.ISysTenantService;
import com.moss.cloud.base.biz.service.ISysUserService;
import com.moss.cloud.common.base.model.po.SysUser;
import com.moss.cloud.common.base.utils.PageUtil;
import com.moss.cloud.common.core.constant.Constant;
import com.moss.cloud.common.core.enums.state.Deleted;
import com.moss.cloud.common.core.exception.SystemErrorType;
import com.moss.cloud.common.core.model.dto.TenantStatusDTO;
import com.moss.cloud.common.core.utils.BooleanHandel;
import com.moss.cloud.common.core.utils.SpringContextHolder;
import com.moss.cloud.common.core.utils.UserContextHolder;
import com.moss.cloud.common.security.annotation.RequiresRole;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;

/**
 * 租户信息业务实现
 *
 * @author 瑾年
 * @date 2023年10月28日
 */
@Slf4j
@Service
public class SysTenantServiceImpl extends ServiceImpl<SysTenantMapper, SysTenant> implements ISysTenantService {

    private ISysUserService sysUserService;
    private ISysTenantLegalService sysTenantLegalService;
    private Executor asyncServiceExecutor;

    public SysTenantServiceImpl(ISysUserService sysUserService, ISysTenantLegalService sysTenantLegalService,
                                Executor asyncServiceExecutor) {
        this.sysUserService = sysUserService;
        this.sysTenantLegalService = sysTenantLegalService;
        this.asyncServiceExecutor = asyncServiceExecutor;
    }

    /**
     * 租户ID查询租户基本信息
     *
     * @return
     */
    @Override
    public TenantInfoVO tenantInfo() {
        Long tenantId = UserContextHolder.getTenantId();
        return this.byTenantId(tenantId);
    }

    /**
     * 租户ID查询租户基本信息
     *
     * @param tenantId
     * @return
     */
    @Override
    @SneakyThrows
    public TenantInfoVO byTenantId(Long tenantId) {
        TenantInfoVO tenantInfoVO = new TenantInfoVO();
        // 租户基本信息
        CompletableFuture<Void> tenant = CompletableFuture.runAsync(() -> {
            TenantInfoVO.Tenant data = new TenantInfoVO.Tenant();
            SysTenant sysTenant = this.getById(tenantId);
            BeanUtils.copyProperties(sysTenant, data);
            tenantInfoVO.setTenant(data);
        }, asyncServiceExecutor);
        // 租户法人信息
        CompletableFuture<Void> tenantLegal = CompletableFuture.runAsync(() -> {
            TenantInfoVO.TenantLegal legal = new TenantInfoVO.TenantLegal();
            TenantLegalVO tenantLegalVO = this.sysTenantLegalService.findTenantLegal(tenantId);
            BeanUtils.copyProperties(tenantLegalVO, legal);
            tenantInfoVO.setTenantLegal(legal);
        }, asyncServiceExecutor);
        CompletableFuture.allOf(tenant, tenantLegal).get();
        return tenantInfoVO;
    }

    public SysTenant tenant(Long tenantId) {
        return this.getById(tenantId);
    }

    /**
     * 用户ID查询租户基本信息
     *
     * @param userId
     * @return
     */
    @Override
    public SysTenant tenantInfoByUserId(Long userId) {
        SysUser sysUser = sysUserService.findByUserId(userId);
        BooleanHandel.trueThrow(Objects.isNull(sysUser)).throwMessage(SystemErrorType.DATE_EMPTY);
        BooleanHandel.trueThrow(Objects.isNull(sysUser.getTenantId())).throwMessage(SystemErrorType.NO_TENANT);
        return this.tenant(sysUser.getTenantId());
    }

    /**
     * 租户分页查询
     *
     * @return
     */
    @Override
    @RequiresRole
    public IPage<TenantPageVO> tenantPage(TenantPageParam tenantPageParam) {
        Long tenantId = UserContextHolder.getTenantId();
        BooleanHandel.trueThrow(!tenantId.equals(Constant.SUPER_TENANT)).throwMessage(SystemErrorType.NO_SUPER_TENANT);
        Page<SysTenant> page = PageUtil.createPage(tenantPageParam.getCurrent(), tenantPageParam.getSize());
        IPage<SysTenant> tenantPage = this.page(page, new QueryWrapper<SysTenant>().lambda()
                .like(StringUtils.isNotBlank(tenantPageParam.getTenantName()), SysTenant::getTenantName, tenantPageParam.getTenantName())
                .like(StringUtils.isNotBlank(tenantPageParam.getTenantPhone()), SysTenant::getTenantPhone, tenantPageParam.getTenantPhone())
                .like(StringUtils.isNotBlank(tenantPageParam.getTenantEmail()), SysTenant::getTenantEmail, tenantPageParam.getTenantEmail())
                .eq(StringUtils.isNotBlank(tenantPageParam.getTenantIndustryType()), SysTenant::getTenantIndustryType, tenantPageParam.getTenantIndustryType())
                .notIn(SysTenant::getDeleted, Deleted.deleteds(Deleted.DELETE.code())));
        List<TenantPageVO> listTenant = CollectionUtils.isNotEmpty(tenantPage.getRecords()) ? tenantPage.getRecords().stream()
                .map(item -> {
                    TenantPageVO tenantPageVO = new TenantPageVO();
                    BeanUtils.copyProperties(item, tenantPageVO);
                    return tenantPageVO;
                }).collect(Collectors.toList()) : Collections.emptyList();
        IPage<TenantPageVO> tenantPageVO = new Page<>();
        BeanUtils.copyProperties(tenantPage, tenantPageVO);
        tenantPageVO.setRecords(listTenant);
        return tenantPageVO;
    }

    /**
     * 删除租户信息
     *
     * @param baseFrom
     * @return
     */
    @Override
    @RequiresRole
    @Transactional(rollbackFor = Exception.class)
    public Boolean removeTenant(BaseFrom baseFrom) {
        SysTenant tenant = this.getById(baseFrom.getId());
        BooleanHandel.trueThrow(Objects.isNull(tenant)).throwMessage(SystemErrorType.DATE_EMPTY);
        // 请在这里实现自己的产品期限验证！请在这里实现自己的产品期限验证！ 请在这里实现自己的产品期限验证！
        tenant.setDeleted(Deleted.DELETE.code());
        this.publishTenantStatusEvent(baseFrom, Deleted.DELETE.code());
        this.updateById(tenant);
        this.publishTenantStatusEvent(baseFrom, Deleted.ENABLE.code());
        return Boolean.TRUE;
    }

    /**
     * 启用，停用租户
     *
     * @param baseFrom
     * @return
     */
    @Override
    @RequiresRole
    @Transactional(rollbackFor = Exception.class)
    public Boolean disabledTenant(BaseFrom baseFrom) {
        SysTenant tenant = this.getById(baseFrom.getId());
        BooleanHandel.trueThrow(Objects.isNull(tenant)).throwMessage(SystemErrorType.DATE_EMPTY);
        // 请在这里实现自己的产品期限验证！请在这里实现自己的产品期限验证！ 请在这里实现自己的产品期限验证！
        BooleanHandel.isTureOrFalse(tenant.getDeleted().equals(Deleted.ENABLE.code())).trueOrFalseHandle(() -> {
            log.info("租户状态为启用:{},改为停用", tenant.getDeleted());
            tenant.setDeleted(Deleted.DISABLE.code());
        }, () -> {
            log.info("租户状态为停用:{},改为启用", tenant.getDeleted());
            tenant.setDeleted(Deleted.ENABLE.code());
        });
        this.updateById(tenant);
        this.publishTenantStatusEvent(baseFrom, Deleted.ENABLE.code());
        return Boolean.TRUE;
    }

    /**
     * 租户状态更新事件
     *
     * @param baseFrom
     * @param status
     */
    private void publishTenantStatusEvent(BaseFrom baseFrom, Integer status) {
        TenantStatusDTO tenantStatusDTO = new TenantStatusDTO();
        tenantStatusDTO.setTenantId(baseFrom.getId());
        tenantStatusDTO.setStatus(status);
        SpringContextHolder.publishEvent(new TenantStatusEvent(this, tenantStatusDTO));
    }

    /**
     * 新增租户
     *
     * @param tenantFrom
     * @return
     */
    @Override
    @RequiresRole
    @Transactional(rollbackFor = Exception.class)
    public Boolean saveTenant(TenantFrom tenantFrom) {
        this.checkTenantPhone(tenantFrom.getTenant().getId(), tenantFrom.getTenant().getTenantName(), Boolean.TRUE);
        SysTenant sysTenant = this.convertSysTenant(tenantFrom);
        boolean saveTenant = this.save(sysTenant);
        SysTenantLegal sysTenantLegal = this.convertSysTenantLegal(tenantFrom);
        sysTenantLegal.setTenantId(sysTenant.getId());
        this.publishEvent(sysTenant.getId(), tenantFrom.getTenantLegal().getTenantUserId());
        // 发布租户更新事件
        tenantFrom.getTenant().setId(sysTenant.getId());
        BooleanHandel.isTrue(saveTenant).trueHandle(() -> this.sysTenantLegalService.save(sysTenantLegal));
        SpringContextHolder.publishEvent(new TenantInfoEvent(this, tenantFrom));
        return Boolean.FALSE;
    }

    /**
     * 更新租户信息
     *
     * @param tenantFrom
     * @return
     */
    @Override
    @RequiresRole
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateTenant(TenantFrom tenantFrom) {
        BooleanHandel.trueThrow(Objects.isNull(tenantFrom.getTenant().getId())).throwMessage(SystemErrorType.PRIMARY_KEY_EMPTY);
        this.checkTenantPhone(tenantFrom.getTenant().getId(), tenantFrom.getTenant().getTenantName(), Boolean.FALSE);
        SysTenant sysTenant = this.convertSysTenant(tenantFrom);
        boolean saveTenant = this.updateById(sysTenant);
        SysTenantLegal sysTenantLegal = this.convertSysTenantLegal(tenantFrom);
        sysTenantLegal.setTenantId(sysTenant.getId());
        BooleanHandel.isTrue(saveTenant).trueHandle(() -> this.sysTenantLegalService.update(sysTenantLegal,
                new QueryWrapper<SysTenantLegal>().lambda().eq(SysTenantLegal::getTenantId, tenantFrom.getTenant().getId())));
        this.publishEvent(sysTenant.getId(), tenantFrom.getTenantLegal().getTenantUserId());
        // 发布租户更新事件
        SpringContextHolder.publishEvent(new TenantInfoEvent(this, tenantFrom));
        return Boolean.FALSE;
    }

    /**
     * 验证租户名称不能重复
     *
     * @param tenantId
     * @param tenantName
     * @param isAdd
     */
    private void checkTenantPhone(Long tenantId, String tenantName, Boolean isAdd) {
        List<SysTenant> sysTenants = this.list(new QueryWrapper<SysTenant>().lambda().eq(SysTenant::getTenantName, tenantName)
                .notIn(SysTenant::getDeleted, Deleted.deleteds(Deleted.DELETE.code())));
        BooleanHandel.isTrue(isAdd.equals(Boolean.TRUE)).trueHandle(() ->
                BooleanHandel.trueThrow(CollectionUtils.isNotEmpty(sysTenants)).throwMessage(SystemErrorType.TENANT_NAME_EXIT));
        BooleanHandel.isTrue(isAdd.equals(Boolean.FALSE)).trueHandle(() -> {
            sysTenants.removeIf(item -> tenantId.equals(item.getId()));
            BooleanHandel.trueThrow(CollectionUtils.isNotEmpty(sysTenants)).throwMessage(SystemErrorType.TENANT_NAME_EXIT);
        });
    }

    /**
     * 转换租户基本信息数据
     *
     * @param tenantFrom
     * @return
     */
    private SysTenant convertSysTenant(TenantFrom tenantFrom) {
        SysTenant sysTenant = new SysTenant();
        BeanUtils.copyProperties(tenantFrom.getTenant(), sysTenant);
        return sysTenant;
    }

    /**
     * 转换法人信息数据
     *
     * @param tenantFrom
     * @return
     */
    private SysTenantLegal convertSysTenantLegal(TenantFrom tenantFrom) {
        SysTenantLegal sysTenantLegal = new SysTenantLegal();
        BeanUtils.copyProperties(tenantFrom.getTenantLegal(), sysTenantLegal);
        return sysTenantLegal;
    }

    /**
     * 发布用户租户更新事件
     *
     * @param tenantId
     * @param userId
     */
    private void publishEvent(Long tenantId, Long userId) {
        UpdateUserTenantFrom updateUserTenantFrom = new UpdateUserTenantFrom();
        updateUserTenantFrom.setUserId(userId);
        updateUserTenantFrom.setTenantId(tenantId);
        SpringContextHolder.publishEvent(new UpdateUserTenantEvent(this, updateUserTenantFrom));
    }
}
